package com.stony.service;

import com.mongodb.*;
import com.stony.enums.MongoTypes;
import com.stony.mapper.HostMapper;
import com.stony.mapper.UserMapper;
import com.stony.model.HostModel;
import com.stony.model.UserModel;
import com.stony.mongo.MongoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.mongodb.core.convert.MongoTypeMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.net.UnknownHostException;
import java.util.*;

/**
 * Created by ShiHui on 2014/6/3.
 */

@Transactional
@Service("hostService")
public class HostService {


    Logger log = LoggerFactory.getLogger(HostService.class);
    private static final int OK = 1;

    @Resource
    private JdbcTemplate jdbcTemplate;


    @Cacheable(value = "session", key = "'allHosts'")
    public List<HostModel> getHosts(){
        String sql = "SELECT id, host, port, type, cluster, shard, repl_set, last_ping, up_since, version " +
                "FROM hosts Order by id";
        List<HostModel> hosts = jdbcTemplate.query(
                sql,
                new HostMapper());
        log.debug("getHosts size: " + hosts.size());

        return hosts;
    }

    @Cacheable(value = "session", key = "#id")
    public HostModel getHost(int id){
        String sql = "SELECT id, host, port, type, cluster, shard, repl_set, last_ping, up_since, version " +
                "FROM hosts WHERE id = ?";
        List<HostModel> hosts = jdbcTemplate.query(
                sql,
                new HostMapper(),
                id);
        log.debug("getHosts size: " + hosts.size());
        if(hosts.size() > 0)
            return hosts.get(0);
        return null;
    }

    @CacheEvict(value = "session", key = "'allHosts'", beforeInvocation = true)
    public boolean saveHost(HostModel host){
        boolean result = false;
        String sql = "INSERT INTO hosts(host, port, type, cluster, shard, repl_set, last_ping, up_since, version) VALUES(?,?,?,?,?,?,?,?,?)";
        MongoClient mongo = null;
        try {
            mongo = new MongoClient(new ServerAddress(host.getHost(),host.getPort()));

            DB db = mongo.getDB("admin");
            if(OK == db.command("ping").getInt("ok")){
                CommandResult cr = getServerStatus(db);
                host.setVersion(MongoUtil.getVersion(cr));
                if(cr.containsField("repl")) {
                    Map repl = (Map) cr.get("repl");
                    if(!repl.isEmpty()){
                        host.setReplSet(repl.get("setName").toString());
                        host.setShard(repl.get("setName").toString());
                        if(repl.get("ismaster").equals(true)){
                            host.setType("primary");
                        }else{
                            host.setType("secondary");
                        }
                    }
                }
                result = OK == jdbcTemplate.update(sql,
                        host.getHost(),
                        host.getPort(),
                        host.getType(),
                        host.getCluster(),
                        host.getShard(),
                        host.getReplSet(),
                        new Date(),
                        new Date(),
                        host.getVersion()
                );
            }
        } catch (UnknownHostException e) {
            log.error(String.format("保存%s%d错误", host.getHost(), host.getPort()), e);
        } finally {
            mongo.close();
        }
        return result;
    }

    public UserModel getUser(UserModel user){
        String sql = "SELECT id, username, password, valid FROM user WHERE username = ? and password = ?";
        List<UserModel> users = jdbcTemplate.query(
                sql,
                new UserMapper(),
                user.getUsername(),
                user.getPassword());
        log.debug("getUser size: " + users.size());
        if(users.size() > 0)
            return users.get(0);
        return null;
    }

    public boolean addUser(String username, String password){
        String sql = "INSERT INTO user(username, password, valid) VALUES (?,?,?)";
        return 1 == jdbcTemplate.update(sql,username,password, 1);
    }



    public Map getDBInfos(String host, int port) {
        MongoClient mongo = null;
        Map<String, Map> dbInfos = new HashMap<String, Map>();
        try {
            mongo = new MongoClient(host, port);
            if(!MongoUtil.isMater(mongo)){
                mongo.setReadPreference(ReadPreference.secondary());
            }
            List<String> dbns = mongo.getDatabaseNames();

            for(String dbn : dbns){
                dbInfos.put(dbn, mongo.getDB(dbn).getStats());
            }
        } catch (UnknownHostException e) {
            log.error(String.format("获取全部DB信息%s%d错误", host, port), e);
        } finally {
            mongo.close();
        }
        return dbInfos;
    }
    public Map getDBAllInfos(String host, int port) {
        MongoClient mongo = null;
        Map<String, Map> dbInfos = new HashMap<String, Map>();
        try {
            mongo = new MongoClient(host, port);
            if(!MongoUtil.isMater(mongo)){
                mongo.setReadPreference(ReadPreference.secondary());
            }
            List<String> dbns = mongo.getDatabaseNames();

            for(String dbn : dbns){
                DB db = mongo.getDB(dbn);
                CommandResult stats = db.getStats();
                stats.put("coll_stats", getCollStats(db));
                dbInfos.put(dbn, stats);
            }
        } catch (UnknownHostException e) {
            log.error(String.format("获取全部DB信息%s%d错误", host, port), e);
        } finally {
            mongo.close();
        }
        return dbInfos;
    }

    @Cacheable(key = "#db.getName().concat(\"_server_status\")", value = "mongoStatsCache")
    public CommandResult getServerStatus(DB db){
        return MongoUtil.getServerStatus(db);
    }

    public Map getCollStats(DB db) {

        Map<String, Map> collStats = new HashMap<String, Map>();
        for (String collName : db.getCollectionNames()) {
            collStats.put(collName, db.getCollection(collName).getStats());
        }
        return collStats;
    }


    public Map getCollInfos(String host, int port, String name) {
        MongoClient mongo = null;
        Map<String, Map> dbInfo = new HashMap<String, Map>();
        try {
            mongo = new MongoClient(host, port);
            if(!MongoUtil.isMater(mongo)){
                mongo.setReadPreference(ReadPreference.secondary());
            }
            DB db = mongo.getDB(name);

            for (String collName : db.getCollectionNames()) {
                dbInfo.put(collName, db.getCollection(collName).getStats());
            }
        } catch (UnknownHostException e) {
            log.error(String.format("获取DB->%s信息%s%d错误", name, host, port), e);
        } finally {
            mongo.close();
        }
        return dbInfo;
    }

    public String findAllDatas(HostModel host,String dbName,String collName){
        MongoClient mongo = null;
        String datas = null;
        try{
            mongo = new MongoClient(host.getHost(), host.getPort());
            if(!MongoUtil.isMater(mongo)){
                mongo.setReadPreference(ReadPreference.secondary());
            }
            DB db = mongo.getDB(dbName);
            DBCollection coll = db.getCollection(collName);
            datas = coll.find().limit(100).toString();

        } catch (UnknownHostException e) {
            log.error(String.format("获取%s->%s数据错误！", dbName, collName), e);
        }finally{
            mongo.close();
        }
        return datas;
    }
    public List<DBObject> findAllData(HostModel host,String dbName,String collName, int skip, int limit,BasicDBObject criteria){
        log.debug("criteria = {}", criteria);
        List<DBObject> datas = new ArrayList<DBObject>();
        MongoClient mongo = null;
        try{
            mongo = new MongoClient(host.getHost(), host.getPort());
            if(!MongoUtil.isMater(mongo)){
                mongo.setReadPreference(ReadPreference.secondary());
            }
            DB db = mongo.getDB(dbName);
            DBCollection coll = db.getCollection(collName);
            if(limit <= 0){
                limit = Integer.MAX_VALUE;
            }
            if(skip > 0){
                datas = coll.find(criteria).skip(skip).limit(limit).toArray();
            }else{
                datas = coll.find(criteria).limit(limit).toArray();
            }
        } catch (UnknownHostException e) {
            log.error(String.format("获取%s->%s数据错误！", dbName, collName), e);
        }finally{
            mongo.close();
        }
        log.info("{}-->{} size : {}", dbName, collName, datas.size());
        return datas;
    }
    public List<DBObject> findAllData(HostModel host,String dbName,String collName, int skip, int limit){
        return findAllData(host, dbName, collName, skip, limit, new BasicDBObject());
    }
    public Set<String> getColumns(HostModel host,String dbName,String collName){
        Set<String> columns = null;
        MongoClient mongo = null;
        try{
            mongo = new MongoClient(host.getHost(), host.getPort());
            if(!MongoUtil.isMater(mongo)){
                mongo.setReadPreference(ReadPreference.secondary());
            }
            DB db = mongo.getDB(dbName);
            DBCollection coll = db.getCollection(collName);
            columns = coll.findOne().keySet();
        } catch (UnknownHostException e) {
            log.error(String.format("获取%s->%s数据错误！", dbName, collName), e);
        }finally{
            mongo.close();
        }
        return columns;
    }
    @Cacheable(value = "contentInfoCache", key = "#host.hashCode()")
    public List getProvinceCount(HostModel host){
        MongoClient mongo = null;
        List infos = new ArrayList();
        try {
            mongo = new MongoClient(host.getHost(), host.getPort());
            DB db = mongo.getDB("logs");

            //优先从Secondary读取
            db.setReadPreference(ReadPreference.secondaryPreferred());
            DBCollection coll = db.getCollection("login_province_count_201404");
            DBCursor cur = coll.find(new BasicDBObject("value.name", new BasicDBObject("$ne","未填")))
                    .sort(new BasicDBObject("value.count", -1));
            infos = cur.toArray();
        } catch (UnknownHostException e) {
            log.error(String.format("获取provinceMap信息%s%d错误", host.getHost(), host.getPort()), e);
        } finally {
            mongo.close();
        }
        return infos;
    }
    public void execMap(DB db){
        if (!db.getCollectionNames().contains("login_province_count_201404")) {
            DBCollection login = db.getCollection("login");

            String mapfun = "function(){emit(this.province, 1)}";
            String reducefun = "function(key, values){return Array.sum(values)}";
            String finalizefun = "function(key, reduceVal){\n" +
                    "\t\t\tvar provinces = {10:\"未填\",11:\"北京\",12:\"天津\",13:\"河北\",14:\"山西\",15:\"内蒙古\",21:\"辽宁\",22:\"吉林\",23:\"黑龙江\",31:\"上海\",32:\"江苏\",33:\"浙江\",34:\"安徽\",35:\"福建\",36:\"江西\",37:\"山东\",41:\"河南\",42:\"湖北\",43:\"湖南\",44:\"广东\",45:\"广西\",46:\"海南\",50:\"重庆\",51:\"四川\",52:\"贵州\",53:\"云南\",54:\"西藏\",61:\"陕西\",62:\"甘肃\",63:\"青海\",64:\"宁夏\",65:\"新疆\",71:\"台湾\",81:\"香港\",82:\"澳门\",99:\"国外\",98:\"美国\"};\n" +
                    "\t\t\tvar id = Number(key,10);\n" +
                    "\t\t\tif(id <= 10){\n" +
                    "\t\t\t\tid = 10;\n" +
                    "\t\t\t}\n" +
                    "\t\t\tvar finalizeVal = {\n" +
                    "\t\t\t\tid : id,\n" +
                    "\t\t\t\tname : (provinces[id] == null ? \"未填\" : provinces[id]),\n" +
                    "\t\t\t\tcount : reduceVal\n" +
                    "\t\t\t}\n" +
                    "\t\t\treturn finalizeVal;\n" +
                    "\t\t}";
            Calendar cal = Calendar.getInstance();
            cal.set(2014,3,1,0,0,0);
            cal.set(Calendar.MILLISECOND, 0);
            Date startDate = cal.getTime();
            cal.set(2014,4,1,0,0,0);
            cal.set(Calendar.MILLISECOND, 0);
            DBObject query = new BasicDBObject("login_time",new BasicDBObject("$gte", startDate).append("$lte", cal.getTime()));

            DBObject command = new BasicDBObject();
            command.put("mapreduce", "logs");
            command.put("map", mapfun);
            command.put("reduce", reducefun);
            command.put("query", query);
            command.put("finalize", finalizefun);
            command.put("out", "login_province_count_201404");
            command.put("jsMode", true);

        }
    }


}
